from PyQt5.Qt import *
from PyUiFile.mainform import Ui_MainWindow
import pandas as pd
import pyqtgraph as pg
from random import randint
import numpy as np
from PyLogicFile.GrayModel import GrayModel
import cgitb
from typing import *
from numba import jit
import time


cgitb.enable(format="text")

pg.setConfigOptions(antialias=True)

qss_str = """
QTableWidget::item:hover {
    background-color: rgba(92, 188, 227, 200)
}

QTableWidget::item:selected {
    background-color: #1B89A1
}

QHeaderView::section, QTableCornerButton:section {
    padding: 3px;
    margin: 0px;
    color: #DCDCDC;
    border: 1px solid #242424;
    border-left-width: 0px;
    border-right-width: 1px;
    border-top-width: 0px;
    border-bottom-width: 1px;
    background: qlineargradient(spread:pad,
    x1: 0,
    y1: 0,
    x2: 0,
    y2: 1,
    stop: 0 #646464,
    stop: 1 #525252);
}

QTableWidget {
    background-color: white;
    border: none
   
}
"""

html_str = """
<style type="text/css">
table.gridtable {
    font-family: verdana,arial,sans-serif;
    font-size:13px;
    color:#333333;
    border-width: 1px;
    border-color: #666666;
    border-collapse: collapse;

}
table.gridtable th {
    border-width: 1px;
    padding: 8px;
    border-style: solid;
    border-color: #666666;
    background-color: #dedede;
}
table.gridtable td {
    border-width: 1px;
    padding: 8px;
    border-style: solid;
    border-color: #666666;
    background-color: #ffffff;
    text-align: center;
}
</style>
<body>
<table class="gridtable">
"""


main_qss = """

"""

@jit(nopython=True)
def calculate_numba(W0, c_begin, c_end, c_span, tao_begin, tao_end, tao_span, T: float,
                    x_time: list, real_data: list, ignore_lst: list):

    x = int((tao_end + tao_span - tao_begin) // tao_span)+1
    y = int((c_end + c_span - c_begin) // c_span)+1

    datas = np.zeros((x*y, len(x_time)))
    relative_err_lst =  np.zeros((x*y, 5))
    relative_err = np.zeros((len(real_data)))

    index = 0
    for tao in np.arange(tao_begin, tao_end + tao_span, tao_span):
        for c in np.arange(c_begin, c_end + c_span, c_span):
            i = 0
            for t in x_time:
                if t >= T:
                    t = T
                if t <= tao:
                    datas[index][i] = W0 * 0.5 * (t - tao * (1 - np.exp(c * t))) / (tao * np.exp(c * tao))
                else:
                    datas[index][i] = W0 * (1 - 0.5 * np.exp(c * (tao - t)))
                i+=1
            c1 = 0
            for i, t in enumerate(x_time[:len(real_data)]):
                if i in ignore_lst:
                    continue
                relative_err[c1] = np.float64(abs(datas[index][i] - real_data[i]) / real_data[i] * 100)
                c1+=1
            # 1. 计算相对残差
            max_err = np.max(relative_err[:c1])
            err_sum = np.sum(relative_err[:c1])
            d = (err_sum, max_err, np.float64(index), c, tao)
            for j in np.arange(5):
                relative_err_lst[index][j] = d[j]
            index += 1
    return datas, relative_err_lst, index


class MainWin(QMainWindow, Ui_MainWindow):

    def __init__(self):
        super().__init__()
        self.setupUi(self)
        centerWidget = self.takeCentralWidget()
        del centerWidget
        self.setDockNestingEnabled(True)
        self.checkBox_reverseAxis.setChecked(True)
        self.df_oriData: pd.DataFrame = None
        self.df_processData: pd.DataFrame = None
        self.tableWidget_SettlementDisplay.setStyleSheet(qss_str)
        self.tableWidget_SettlementDisplay_2.setStyleSheet(qss_str)
        self.tableWidget.setStyleSheet(qss_str)
        # 获取用户窗口分辨率
        self.screenRect = QApplication.desktop().screenGeometry()
        self.height = self.screenRect.height()
        self.width = self.screenRect.width()
        print("您的屏幕分辨率为", self.width ,"*", self.height)


        # 单独设置沉降值计算窗口
        # self.tableWidget_SettlementDisplay.action_drawFigure.setVisible(False)
        self.tableWidget_SettlementDisplay.action_CalculateSettlement.setVisible(False)
        self.tableWidget_SettlementDisplay.action_insertcol.setVisible(False)
        self.tableWidget_SettlementDisplay.propertyWin.spinBox_colnum.setEnabled(False)

        self.tableWidget_SettlementDisplay_2.action_CalculateSettlement.setVisible(False)
        self.tableWidget_SettlementDisplay_2.action_insertcol.setVisible(False)
        self.tableWidget_SettlementDisplay_2.propertyWin.spinBox_colnum.setEnabled(False)
        self.tableWidget_SettlementDisplay_2.setRowCount(100)
        self.tableWidget_SettlementDisplay_2.setColumnCount(2)
        self.tableWidget_SettlementDisplay_2.setHorizontalHeaderLabels(['时间(h)', '沉降值(mm)'])

        # self.tableWidget_SettlementDisplay.action_insertrow.setVisible(False)

        # 右键菜单设置
        self.tableWidget_SettlementDisplay.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tableWidget_SettlementDisplay_2.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.textBrowser_2.setContextMenuPolicy(Qt.CustomContextMenu)
        self.tableWidget.propertyWin.tableWidget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.textBrowser.setContextMenuPolicy(Qt.CustomContextMenu)

        self.radioButtonOriData.setChecked(True)

        # 菜单项图片设置
        self.action_datafromExcel.setIcon(QIcon("Images/导入数据.png"))
        self.action_outputGrayModelData.setIcon(QIcon("Images/导出数据.png"))
        self.action_newTable.setIcon(QIcon("Images/新建表格.png"))

        # 工具栏设置
        self.toolBar = QToolBar(self)
        self.addToolBar(Qt.LeftToolBarArea, self.toolBar)
        self.toolBar.addAction(self.action_newTable)
        self.toolBar.addAction(self.action_datafromExcel)
        self.action_datafromExcel.setToolTip("从Excel文件导入数据")
        self.toolBar.addAction(self.action_outputGrayModelData)
        self.action_outputGrayModelData.setToolTip("灰色理论预测数据导出")
        self.toolBar.setAllowedAreas(Qt.LeftToolBarArea)
        # 布局
        self.customLayout()

        # 基础设置
        self.tableWidget_SettlementDisplay_2.action_drawFigure.setVisible(False)
        self.timer = QTimer(self)
        self.draw_cnt = 0
        self.radioButton_moshi1.setChecked(True)
        self.before_changed = "模式一"
        self.exist_data_flag = False
        self.label_Show_c.clear()
        self.label_show_tao.clear()
        self.label_showRelativeErrSum.clear()
        self.label_showError.clear()

        # 测试
        self.test()
        self.inputData()
        self.tableWidget.selectAll()
        self.calculateSettlement()
        self.tableWidget_SettlementDisplay.selectAll()
        self.theoryCalculationPrediction()
        self.dockWidget_5.raise_()

        # Qss样式设置
        self.setStyleSheet(main_qss)

        # 槽函数链接
        self.connectSlotFunction()

        # 最大化显示窗口
        self.showMaximized()

    def clearOutput(self):
        self.textBrowser.clear()

    def connectSlotFunction(self):
        self.action_exit.triggered.connect(self.deleteLater)
        self.action_newTable.triggered.connect(self.newTable)
        self.action_datafromExcel.triggered.connect(self.inputData)
        self.radioButton_ProcessData.toggled[bool].connect(self.changeData1)
        self.radioButtonOriData.toggled[bool].connect(self.changeData2)
        self.pushButton_draw.clicked.connect(self.drawOriginData)
        self.pushButton_reset.clicked.connect(self.ori_reset)
        self.pushButton_clearPicture.clicked.connect(self.ori_plot_win.plotItem.clear)
        self.tableWidget.Signal_CalculateSettlement.connect(self.calculateSettlement)
        self.tableWidget_SettlementDisplay.action_drawFigure.disconnect()
        self.tableWidget_SettlementDisplay.action_drawFigure.triggered.connect(self.drawSettlementFigure)
        self.action_RunGrayTheory.triggered.connect(self.getGrayPredictionResult)
        self.action_TheoryCalculationPrediction.triggered.connect(self.theoryCalculationPrediction)
        self.action_CustomParameterFit.triggered.connect(self.customParameterFit)
        self.action_DynamicFit.triggered.connect(self.dynamicFit)
        self.timer.timeout.connect(self.start)
        self.radioButton_moshi1.toggled[bool].connect(self.moshi1_StateChanged)
        self.radioButton_moshi2.toggled[bool].connect(self.moshi2_StateChanged)
        self.pushButton_continue.clicked.connect(self.fit_continue)
        self.pushButton_suspend.clicked.connect(self.fit_suspend)
        self.pushButton_stop.clicked.connect(self.fit_stop)
        self.pushButton_shuchudata.clicked.connect(self.showFitBestData)


    def showFitBestData(self):
        if self.exist_data_flag:
            self.outputToUser_2("Knothe时间函数动态拟合最佳参数结果如下:", level=2)
            err = self.err_lsts[0]
            self.label_Show_c.setText("{:.3f}".format(err[3]))
            self.label_show_tao.setText("{:.1f}".format(err[4]))
            self.label_showRelativeErrSum.setText("{:.3f}".format(err[0]))
            self.label_showError.setText("{:.3f}".format(err[1]))
            parameters = {
                "W0":[self.W0_danymic],
                "T":[self.T_dynamic],
                "c":["{:.3f}".format(err[3])],
                "τ":["{:.1f}".format(err[4])]
            }

            parameters_df = self.dictToDataframe(parameters)
            parameters_html = self.ParamDataFrameToHtml(parameters_df)
            self.textBrowser_2.append(parameters_html)
            self.outputToUser_2("其预测结果如下:", level=2)
            dct = {}
            dct['时间'] = self.x_time
            dct['实际值'] = self.real_data
            dct['预测值'] = list(map(lambda x: round(x, 3), self.datas[int(self.err_lsts[0][2])]))
            dct['误差'] = list(map(lambda x: round(x, 3), np.array(dct['预测值'][:len(dct['实际值'])]) - np.array(dct['实际值'])))
            dct['相对误差(%)'] = list(map(lambda x: round(x, 3),
                                      np.abs(np.array(dct['预测值'][:len(dct['实际值'])] - np.array(dct['实际值'])) / np.array(
                                          dct['实际值']) * 100)))

            df = self.dictToDataframe(dct)
            html = self.DataFrameToHtml(df, -1)
            self.textBrowser_2.append(html)

    def fit_continue(self):
        if self.exist_data_flag:
            self.timer.start(1)

    def fit_suspend(self):
        if self.exist_data_flag:
            self.timer.stop()
    def fit_stop(self):
        if self.exist_data_flag:
            self.plot.setData(self.datas[int(self.err_lsts[0][2])])
            # (err_sum, max_err, np.float64(index), c, tao)

            err = self.err_lsts[0]
            self.label_Show_c.setText("{:.3f}".format(err[3]))
            self.label_show_tao.setText("{:.1f}".format(err[4]))
            self.label_showRelativeErrSum.setText("{:.3f}".format(err[0]))
            self.label_showError.setText("{:.3f}".format(err[1]))
            self.timer.stop()
            self.draw_cnt = 0



    def moshi1_StateChanged(self, checked:bool):
        if self.before_changed == "模式一":
            if not checked:  # 选中模式二
                self.draw_cnt = 0
                self.before_changed = "模式二"
                print("模式二选中")



    def moshi2_StateChanged(self, checked:bool):
        if self.before_changed == "模式二":
            if not checked:  # 选中模式二
                self.draw_cnt = 0
                self.before_changed = "模式一"
                print("模式一选中")

    def drawSettlementFigure(self):
        selections = self.tableWidget_SettlementDisplay.selectedRanges()
        if len(selections) == 0:
            return
        self.predictPlotWin.addLegend(offset=(50, 50))
        # self.ori_plot_win.plotItem.showGrid(x=True, y=True, alpha = 0.4)  # 设置绘图部件显示网格线
        self.predictPlotWin.plotItem.clear()
        labels = [self.tableWidget_SettlementDisplay.horizontalHeaderItem(i).text() for i in
                  range(self.tableWidget_SettlementDisplay.columnCount())]
        data = []
        for selection in selections:
            toprow = selection.topRow()
            bottomrow = selection.bottomRow()
            leftcolumn = selection.leftColumn()
            rightcolumn = selection.rightColumn()
            for col in range(leftcolumn, rightcolumn + 1, 1):
                lst = []
                for row in range(toprow, bottomrow + 1, 1):
                    lst.append(float(self.tableWidget_SettlementDisplay.item(row, col).text()))
                data.append((labels[col], lst))

        for index in range(1, len(data)):
            self.predictPlotWin.plotItem.plot(x=data[0][1], y=data[index][1],
                                              pen=(randint(0, 255), randint(0, 255), randint(0, 255)),
                                              symbolBrush=(randint(0, 255), randint(0, 255), randint(0, 255)),
                                              symbolPen='w', symbol='o', symbolSize=7)

        self.predictPlotWin.setLabel('left', self.tableWidget_SettlementDisplay.horizontalHeaderItem(1).text())
        self.predictPlotWin.setLabel('bottom', self.tableWidget_SettlementDisplay.horizontalHeaderItem(0).text())

    def calculateSettlement(self):
        selections = self.tableWidget.selectedRanges()
        if len(selections) == 0:
            return

        if len(selections) >= 2:
            QMessageBox.warning(self, "错误提示", "您不能对多重选择区域进行此操作")
            return
        selection = selections[0]
        toprow = selection.topRow()
        bottomrow = selection.bottomRow()
        leftcolumn = selection.leftColumn()
        rightcolumn = selection.rightColumn()
        labels = [self.tableWidget.horizontalHeaderItem(i).text() for i in range(self.tableWidget.columnCount())]
        datas = []
        x_lst = []
        for j in range(toprow, bottomrow + 1):
            x_lst.append(self.tableWidget.item(j, leftcolumn).text())
        x_lst = list(map(float, x_lst))

        for i in range(leftcolumn + 1, rightcolumn + 1):
            lst = []
            title = labels[i]
            for j in range(toprow, bottomrow + 1):
                lst.append(self.tableWidget.item(j, i).text())
            lst = list(map(float, lst))
            ret = abs(np.trapz(lst, x_lst, dx=0.01) / 1000)
            datas.append((title, round(ret, 3)))

        self.tableWidget_SettlementDisplay.setColumnCount(2)
        self.tableWidget_SettlementDisplay.setRowCount(len(datas))
        self.tableWidget_SettlementDisplay.setHorizontalHeaderLabels(['开采范围(cm)', '沉降值(mm)'])

        for j in range(len(datas)):
            self.tableWidget_SettlementDisplay.setItem(j, 0, QTableWidgetItem(str(datas[j][0])))
            self.tableWidget_SettlementDisplay.setItem(j, 1, QTableWidgetItem(str(datas[j][1])))
        self.dockWidget_3.raise_()

    def customLayout(self):
        self.addDockWidget(Qt.RightDockWidgetArea, self.dockWidget_4)
        self.setLayoutDirection(Qt.LeftToRight)
        self.dockWidget_2.setAllowedAreas(Qt.LeftDockWidgetArea)
        self.dockWidget.setAllowedAreas(Qt.LeftDockWidgetArea)
        self.dockWidget_3.setAllowedAreas(Qt.LeftDockWidgetArea)
        self.tabifyDockWidget(self.dockWidget, self.dockWidget_2)
        self.tabifyDockWidget(self.dockWidget, self.dockWidget_3)
        self.tabifyDockWidget(self.dockWidget, self.dockWidget_5)

        self.dockWidget.raise_()
        self.splitter_2.setStretchFactor(0, 3.5)
        self.splitter_2.setStretchFactor(1, 6.5)
        self.splitter_3.setStretchFactor(0, 7)
        self.splitter_3.setStretchFactor(1, 3)

    def ori_reset(self):
        self.lineEdit_horizontalAxisTitle.clear()
        self.lineEdit_verticalAxisTitle.clear()
        self.lineEdit_verticalAxisUnits.clear()
        self.lineEdit_horizontalAxisUnits.clear()
        self.checkBox_reverseAxis.setChecked(True)

    def drawOriginData(self):

        selections = self.tableWidget.selectedRanges()
        self.ori_plot_win.addLegend(offset=(50, 50))
        # self.ori_plot_win.plotItem.showGrid(x=True, y=True, alpha = 0.4)  # 设置绘图部件显示网格线
        self.ori_plot_win.plotItem.clear()
        labels = [self.tableWidget.horizontalHeaderItem(i).text() for i in
                  range(self.tableWidget.columnCount())]
        data = []
        for selection in selections:
            toprow = selection.topRow()
            bottomrow = selection.bottomRow()
            leftcolumn = selection.leftColumn()
            rightcolumn = selection.rightColumn()
            for col in range(leftcolumn, rightcolumn + 1, 1):
                lst = []
                for row in range(toprow, bottomrow + 1, 1):
                    lst.append(float(self.tableWidget.item(row, col).text()))
                data.append((labels[col], lst))
        # 设置坐标轴信息
        xtitle = self.lineEdit_horizontalAxisTitle.text()
        xunits = self.lineEdit_horizontalAxisUnits.text()
        ytitle = self.lineEdit_verticalAxisTitle.text()
        yunits = self.lineEdit_verticalAxisUnits.text()
        if xtitle == "":
            x = ""
        else:
            if xunits == "":
                x = xtitle
            else:
                x = "{}({})".format(xtitle, xunits)

        if ytitle == "":
            y = ""
        else:
            if yunits == "":
                y = ytitle
            else:
                y = "{}({})".format(ytitle, yunits)
        if self.checkBox_reverseAxis.isChecked():
            self.ori_plot_win.setLabel('left', y)
            self.ori_plot_win.setLabel('bottom', x)
            # 绘图
            for index in range(1, len(data)):
                self.ori_plot_win.plotItem.plot(y=data[0][1], x=data[index][1], name=data[index][0],
                                                pen=(randint(0, 255), randint(0, 255), randint(0, 255)))
        else:
            self.ori_plot_win.setLabel('left', y)
            self.ori_plot_win.setLabel('bottom', x)
            # 绘图
            for index in range(1, len(data)):
                self.ori_plot_win.plotItem.plot(x=data[0][1], y=data[index][1], name=data[index][0],
                                                pen=(randint(0, 255), randint(0, 255), randint(0, 255)))

    def changeData1(self, val: bool):
        try:
            if val:
                self.tableWidget.clear()
                df = self.df_processData
                col_num = len(df.columns)
                row_num = len(df.index)
                self.tableWidget.setRowCount(row_num)
                self.tableWidget.setColumnCount(col_num)
                labels = list(df.columns)
                labels = map(str, labels)
                self.tableWidget.setHorizontalHeaderLabels(labels)
                for i in range(row_num):
                    for j in range(col_num):
                        self.tableWidget.setItem(i, j, QTableWidgetItem(str(df.iloc[i, j])))

        except Exception as e:
            print(e, e.__traceback__.tb_frame.f_globals["__file__"], e.__traceback__.tb_lineno, sep='\t\t\t')

    def changeData2(self, val: bool):
        try:
            if val:
                self.tableWidget.clear()
                df = self.df_oriData
                col_num = len(df.columns)
                row_num = len(df.index)
                self.tableWidget.setRowCount(row_num)
                self.tableWidget.setColumnCount(col_num)
                labels = list(df.columns)
                labels = map(str, labels)
                self.tableWidget.setHorizontalHeaderLabels(labels)
                for i in range(row_num):
                    for j in range(col_num):
                        self.tableWidget.setItem(i, j, QTableWidgetItem(str(df.iloc[i, j])))
        except Exception as e:
            print(e, e.__traceback__.tb_frame.f_globals["__file__"], e.__traceback__.tb_lineno, sep='\t\t\t')

    def inputData(self):
        # filename, ok = QFileDialog.getOpenFileName(self, "数据导入", ".", "Excel Files(*.xlsx *.xls)")
        filename = r"C:\Users\hejinyang\Desktop\V1.xlsx"
        ok = True
        try:
            if ok:
                df = pd.read_excel(filename)
                col_num = len(df.columns)
                row_num = len(df.index)
                self.tableWidget.setRowCount(row_num)
                self.tableWidget.setColumnCount(col_num)
                labels = list(df.columns)
                labels = map(str, labels)
                self.tableWidget.setHorizontalHeaderLabels(labels)
                self.df_oriData = df
                data = {}
                data[df.columns[0]] = df[df.columns[0]]
                data[df.columns[1]] = df[df.columns[1]]
                for i in range(2, col_num):
                    series = df[df.columns[i]] - df[df.columns[1]]
                    data[df.columns[i]] = list(map(lambda x: round(x, 5), series.tolist()))
                self.df_processData = pd.DataFrame(data)
                if self.radioButtonOriData.isChecked():
                    for i in range(row_num):
                        for j in range(col_num):
                            self.tableWidget.setItem(i, j, QTableWidgetItem(str(df.iloc[i, j])))
                elif self.radioButton_ProcessData.isChecked():
                    new_df = self.df_processData
                    for i in range(row_num):
                        for j in range(col_num):
                            self.tableWidget.setItem(i, j, QTableWidgetItem(str(new_df.iloc[i, j])))
        except Exception as e:
            print(e, e.__traceback__.tb_frame.f_globals["__file__"], e.__traceback__.tb_lineno, sep='\t\t\t')

    def newTable(self):
        self.tableWidget.clear()
        self.tableWidget.setColumnCount(20)
        self.tableWidget.setRowCount(100)
        self.tableWidget.setHorizontalHeaderLabels(["untitled" + str(i) for i in range(20)])

    def getGrayPredictionResult(self):
        # 1. 获取预测参数
        train_data_length = self.SpinBoxTrainCount.value()
        predict_length = self.SpinBoxPredictCount.value()
        if predict_length == 0:
            self.outputToUser('预测次数为0', 1)
            return
            # 2. 获取数据
        selections = self.tableWidget_SettlementDisplay.selectedRanges()
        if len(selections) == 0:
            self.outputToUser('您没有选择任何数据, 请重新选择后再试', 1)
            return

        if len(selections) >= 2:
            QMessageBox.critical(self, "出现错误", "您不能对多重区域进行此操作", QMessageBox.Yes)
            return

        data = []
        selection = selections[0]
        toprow = selection.topRow()
        bottomrow = selection.bottomRow()
        leftcolumn = selection.leftColumn()
        rightcolumn = selection.rightColumn()
        for col in range(leftcolumn, rightcolumn + 1, 1):
            lst = []
            for row in range(toprow, bottomrow + 1, 1):
                try:
                    lst.append(float(self.tableWidget_SettlementDisplay.item(row, col).text()))
                except Exception:
                    break
            data.append(lst)

        labels = [self.tableWidget_SettlementDisplay.horizontalHeaderItem(i).text() for i in
                  range(self.tableWidget_SettlementDisplay.columnCount())]
        df: pd.DataFrame = None
        if len(data) == 1:
            predict_data = data[0]
            df = None
        elif len(data) == 2:
            df = pd.DataFrame({labels[0]: data[0]})
            predict_data = data[1]
        else:
            self.outputToUser('预测数据选择错误, 请重新选择', 1)
            return

        if train_data_length < 3:
            self.outputToUser('模型训练集数据至少三个, 您的输入有误, 已经使用默认值3', 2)
            self.SpinBoxTrainCount.setValue(3)
            train_data_length = 3

        if not any([self.checkBox_CommonGrayModel.isChecked(), self.checkBox_SupplementModel.isChecked(),
                    self.checkBox_MetabolismModel.isChecked()]):
            self.outputToUser('您还未选择灰色理论预测模型, 请选择后再试!', 1)
            return
        # 建模
        graymodel = GrayModel(np.array(predict_data[:train_data_length]),
                              np.array(predict_data[train_data_length:train_data_length + predict_length]),
                              predict_length)
        self.outputToUser(
            f'本次预测使用的训练集数据为{predict_data[:train_data_length]}；<br> 测试集数据为{predict_data[train_data_length:train_data_length + predict_length]}；<br> 预测次数为{predict_length}；',
            level=4)

        # 绘实际值得散点图
        self.predictPlotWin.addLegend(offset=(50, 50))
        self.predictPlotWin.plotItem.clear()
        labels = [self.tableWidget_SettlementDisplay.horizontalHeaderItem(i).text() for i in
                  range(self.tableWidget_SettlementDisplay.columnCount())]
        data = []
        for col in range(leftcolumn, rightcolumn + 1, 1):
            lst = []
            for row in range(toprow, bottomrow + 1, 1):
                try:
                    lst.append(float(self.tableWidget_SettlementDisplay.item(row, col).text()))
                except Exception:
                    break
            data.append((labels[col], lst))

        self.predictPlotWin.plotItem.plot(x=data[0][1], y=data[1][1],
                                          pen=None,
                                          symbolBrush='b',
                                          symbolPen='w', symbol='o', symbolSize=7, name='实际值')

        self.predictPlotWin.setLabel('left', self.tableWidget_SettlementDisplay.horizontalHeaderItem(1).text())
        self.predictPlotWin.setLabel('bottom', self.tableWidget_SettlementDisplay.horizontalHeaderItem(0).text())

        if self.checkBox_CommonGrayModel.isChecked():
            ret = graymodel.ordinaryGMPredict()
            if df is not None:
                ret = pd.concat([df, ret], axis=1)
                ret = ret.fillna("")
            content: str = self.DataFrameToHtml(ret, graymodel.train_length)
            self.outputToUser('采用普通GM(1,1)模型预测结果如下:', level=2)
            self.textBrowser.append(content)

            dct = {}
            dct['模型参数'] = ('发展系数a', '灰色作用量b')
            dct['计算结果'] = (round(graymodel.a, 4), round(graymodel.b, 4))
            content = self.ParamDataFrameToHtml(pd.DataFrame(dct))
            self.outputToUser('普通GM(1,1)模型求解参数如下:', level=2)
            self.textBrowser.append(content)

            x_data: List = data[0][1]
            y = ret['预测值']
            if len(x_data) > len(y):
                x_data = x_data[:len(y)]
            else:
                last = x_data[-1]
                span = x_data[1] - x_data[0]
                for i in range(len(y) - len(x_data)):
                    x_data.append(last + span)
                    last = x_data[-1]
            self.predictPlotWin.plotItem.plot(x=x_data, y=y,
                                              pen=(195, 46, 212),
                                              symbolBrush=(randint(0, 255), randint(0, 255), randint(0, 255)),
                                              symbolPen='w', symbol='+', symbolSize=7, name='普通模型预测值')

        if self.checkBox_SupplementModel.isChecked():
            ret = graymodel.getGrayNumberFillModelResult()
            if df is not None:
                # df = df[train_data_length:].reset_index(drop=True)
                ret = pd.concat([df, ret], axis=1)
                ret = ret.fillna("")
            content: str = self.DataFrameToHtml(ret, graymodel.train_length)
            self.outputToUser('采用灰数递补模型预测结果如下:', level=2)
            self.textBrowser.append(content)

            x_data: List = data[0][1]
            y = ret['预测值']
            if len(x_data) > len(y):
                x_data = x_data[:len(y)]
            else:
                last = x_data[-1]
                span = x_data[1] - x_data[0]
                for i in range(len(y) - len(x_data)):
                    x_data.append(last + span)
                    last = x_data[-1]
            self.predictPlotWin.plotItem.plot(x=x_data, y=y,
                                              pen=(237, 177, 32),
                                              symbolBrush=(randint(0, 255), randint(0, 255), randint(0, 255)),
                                              symbolPen='w', symbol='p', symbolSize=7, name='灰数递补模型预测值')

        if self.checkBox_MetabolismModel.isChecked():
            if predict_length > len(predict_data) - train_data_length:
                self.outputToUser('您选择了新陈代谢模型, 该模型需要实测得数据进行迭代计算, 您提供的数据不足, 无法计算!', level=1)
            else:
                ret = graymodel.getMetabolicModelResult()
                if df is not None:
                    # df = df[train_data_length:].reset_index(drop=True)
                    ret = pd.concat([df, ret], axis=1)
                    ret = ret.fillna("")
                content: str = self.DataFrameToHtml(ret, graymodel.train_length)
                self.outputToUser('采用新陈代谢模型预测结果如下:', level=2)
                self.textBrowser.append(content)

                x_data: List = data[0][1]
                y = ret['预测值']
                if len(x_data) > len(y):
                    x_data = x_data[:len(y)]
                else:
                    last = x_data[-1]
                    span = x_data[1] - x_data[0]
                    for i in range(len(y) - len(x_data)):
                        x_data.append(last + span)
                        last = x_data[-1]
                self.predictPlotWin.plotItem.plot(x=x_data, y=y,
                                                  pen=(19, 234, 201),
                                                  symbolBrush=(randint(0, 255), randint(0, 255), randint(0, 255)),
                                                  symbolPen='w', symbol='star', symbolSize=7, name='新陈代谢模型预测值')

        dct = {}
        dct['项目'] = ['相对误差', '小误差概率', '均方差比值']
        res = graymodel.getAccuracy()
        dct['计算结果'] = [res.get('相对残差检验')[0], res.get('小误差概率检验')[0], res.get('均方差比值检验')[0]]
        dct['精度'] = [res.get('相对残差检验')[1], res.get('小误差概率检验')[1], res.get('均方差比值检验')[1]]
        accuracyDf = pd.DataFrame(dct)
        self.outputToUser('模型训练精度如下:', level=2)
        content = self.AccuracyDataFrameToHtml(accuracyDf)
        self.textBrowser.append(content)
        self.textBrowser.append(
            '<b>以上预测结果中, 黑色表示模型训练的结果; <font size="3" color="green">绿色</font>表示计算误差合格的预测结果或模型训练精度合格; <font size="3" color="red">红色</font>表示计算误差不合格的预测结果或模型训练精度不合格; <font size="3" color="#aaa6ad"><i>灰色斜体</i></font>表示没有实际数据, 无法计算误差值! </b>')

    def outputToUser(self, text: str, level: int = 4, bold: bool = True):
        """
        :param text: 输出的内容
        :param level:
        `1`: red
        `2`: blue
        `3`: green
        `4` : black
        :return:
        """
        if level == 4:
            color = 'black'
        elif level == 3:
            color = 'green'
        elif level == 2:
            color = 'blue'
        elif level == 1:
            color = 'red'
        else:
            color = 'black'
        if bold:
            s = f'<font color={color} size="3"><b>{text}</b></font>'
        else:
            s = f'<font color={color} size="3">{text}</font>'
        self.textBrowser.append(s)

    def outputToUser_2(self, text: str, level: int = 4, bold: bool = True):
        """
        :param text: 输出的内容
        :param level:
        `1`: red
        `2`: blue
        `3`: green
        `4` : black
        :return:
        """
        if level == 4:
            color = 'black'
        elif level == 3:
            color = 'green'
        elif level == 2:
            color = 'blue'
        elif level == 1:
            color = 'red'
        else:
            color = 'black'
        if bold:
            s = f'<font color={color} size="3"><b>{text}</b></font>'
        else:
            s = f'<font color={color} size="3">{text}</font>'
        self.textBrowser_2.append(s)

    def test(self):
        self.dockWidget_5.raise_()
        self.checkBox_SupplementModel.setChecked(True)
        self.tableWidget_SettlementDisplay.setRowCount(10)
        self.tableWidget_SettlementDisplay.setColumnCount(2)
        self.tableWidget_SettlementDisplay.setHorizontalHeaderLabels(['开挖范围', '沉降值'])
        self.tableWidget_SettlementDisplay.setItem(0, 0, QTableWidgetItem(str(30)))
        self.tableWidget_SettlementDisplay.setItem(1, 0, QTableWidgetItem(str(40)))
        self.tableWidget_SettlementDisplay.setItem(2, 0, QTableWidgetItem(str(50)))
        self.tableWidget_SettlementDisplay.setItem(3, 0, QTableWidgetItem(str(60)))
        self.tableWidget_SettlementDisplay.setItem(4, 0, QTableWidgetItem(str(70)))
        self.tableWidget_SettlementDisplay.setItem(5, 0, QTableWidgetItem(str(80)))
        self.tableWidget_SettlementDisplay.setItem(6, 0, QTableWidgetItem(str(90)))
        self.tableWidget_SettlementDisplay.setItem(7, 0, QTableWidgetItem(str(100)))
        self.tableWidget_SettlementDisplay.setItem(8, 0, QTableWidgetItem(str(110)))
        self.tableWidget_SettlementDisplay.setItem(9, 0, QTableWidgetItem(str(120)))

        self.tableWidget_SettlementDisplay.setItem(0, 1, QTableWidgetItem(str(1.6053)))
        self.tableWidget_SettlementDisplay.setItem(1, 1, QTableWidgetItem(str(2.776)))
        self.tableWidget_SettlementDisplay.setItem(2, 1, QTableWidgetItem(str(3.9751)))
        self.tableWidget_SettlementDisplay.setItem(3, 1, QTableWidgetItem(str(4.4792)))
        self.tableWidget_SettlementDisplay.setItem(4, 1, QTableWidgetItem(str(5.5745)))
        self.tableWidget_SettlementDisplay.setItem(5, 1, QTableWidgetItem(str(7.1783)))
        self.tableWidget_SettlementDisplay.setItem(6, 1, QTableWidgetItem(str(8.889)))
        self.tableWidget_SettlementDisplay.setItem(7, 1, QTableWidgetItem(str(10.9843)))
        self.tableWidget_SettlementDisplay.setItem(8, 1, QTableWidgetItem(str(12.7529)))
        self.tableWidget_SettlementDisplay.setItem(9, 1, QTableWidgetItem(str(13.1359)))
        self.SpinBoxPredictCount.setValue(5)
        self.SpinBoxTrainCount.setValue(6)
        self.checkBox_CommonGrayModel.setChecked(True)

        self.lineEdit_FinalSettlement.setText("14")
        self.lineEdit_DigSpeed.setText("0.05")
        self.lineEdit_AverageDigDepth.setText("0.8")
        self.lineEdit_OffsetDistance.setText("0.06")
        self.lineEdit_MainInfluentialAngel.setText("57")
        self.lineEdit_CoalVerticalThickness.setText("0.042")
        self.lineEdit_CoalAngel.setText("0")
        self.lineEdit_GetDataTimeInterval.setText("1")

        self.lineEdit_W0_custom.setText("14")
        self.lineEdit_c_custom.setText("0.187")
        self.lineEdit_tao_custom.setText("13.44")
        self.lineEdit_GetDataTimeInterval_custom.setText("1")
        self.lineEdit_GroundMoveTotalTime_custom.setText("48")

        lst = [3.399, 0.047, 0.389, 0.723, 1.095, 1.605, 2.192, 2.776, 3.425, 3.975, 4.16, 4.479, 4.948, 5.575, 6.41,
               7.178, 8.06, 8.889, 10.01, 10.984, 12.515, 12.753, 12.895, 13.136, 13.136, 12.738, 12.659, 12.817]
        for index, val in enumerate(lst):
            self.tableWidget_SettlementDisplay_2.setItem(index, 0, QTableWidgetItem(str(index + 1)))
            self.tableWidget_SettlementDisplay_2.setItem(index, 1, QTableWidgetItem(str(val)))
        self.tableWidget_SettlementDisplay_2.selectAll()

        self.lineEdit_FitW0.setText("14")
        self.doubleSpinBox_taoBegin.setValue(12)
        self.doubleSpinBox_taoEnd.setValue(20)
        self.doubleSpinBox_taoChangeStep.setValue(0.01)

        self.doubleSpinBox_CBegin.setValue(0.100)
        self.doubleSpinBox_CEnd.setValue(0.500)
        self.doubleSpinBox_CChangeStep.setValue(0.01)
        self.lineEdit_T.setText("48")
        self.lineEdit_timeinterval.setText('1')

    @staticmethod
    def DataFrameToHtml(df: pd.DataFrame, fit_num: int):
        content = """"""
        columns = df.columns
        content += "<tr>"
        for title in columns:
            content += "<th>{}</th>".format(title)
        content += "</tr>"
        cnt = 0
        for i in range(len(df.index)):
            content += "<tr>"
            for j in range(len(columns)):
                if cnt < fit_num:
                    content += '<td><b>{}</b></td>'.format(df.iloc[i, j])
                else:
                    if isinstance(df.loc[i, '相对误差(%)'], str):
                        content += '<td><b><i><font color="#aaa6ad">{}</font></i></b></td>'.format(df.iloc[i, j])
                    else:
                        if round(df.loc[i, '相对误差(%)'], 3) > 20:
                            content += '<td><font color="red"><b>{}</b></font></td>'.format(df.iloc[i, j])
                        else:
                            content += '<td><font color="green"><b>{}</b></font></td>'.format(df.iloc[i, j])
            cnt += 1
            content += "</tr>"
        content += "</table></body>"
        return html_str + content

    @staticmethod
    def AccuracyDataFrameToHtml(df: pd.DataFrame):
        content = """"""
        columns = df.columns
        content += "<tr>"
        for title in columns:
            content += "<th>{}</th>".format(title)
        content += "</tr>"
        for i in range(len(df.index)):
            content += "<tr>"
            for j in range(len(columns)):

                if df.loc[i, '精度'] in ['一级', '二级', '三级']:
                    content += '<td><font color="green"><b>{}</b></font></td>'.format(df.iloc[i, j])
                else:
                    content += '<td><font color="red"><b>{}</b></font></td>'.format(df.iloc[i, j])
            content += "</tr>"
        content += "</table></body>"
        return html_str + content

    @staticmethod
    def ParamDataFrameToHtml(df: pd.DataFrame):
        content = """"""
        columns = df.columns
        content += "<tr>"
        for title in columns:
            content += "<th>{}</th>".format(title)
        content += "</tr>"
        for i in range(len(df.index)):
            content += "<tr>"
            for j in range(len(columns)):
                content += '<td><b>{}</b></td>'.format(df.iloc[i, j])
            content += "</tr>"
        content += "</table></body>"
        return html_str + content

    def theoryCalculationPrediction(self):

        # 1. 沉降值
        try:
            self.W0 = float(self.lineEdit_FinalSettlement.text())
        except Exception:
            self.outputToUser_2("最终沉降值未设置或设置错误, 将根据《三下采煤规程》计算沉降值。", level=1)
            self.W0 = None

        # 2. 煤层开采速度
        try:
            self.coal_dig_speed = float(self.lineEdit_DigSpeed.text())
        except Exception:
            self.outputToUser_2("煤层开采速度未设置或设置错误!", level=1)
            return

        # 3. 煤层平均采深
        try:
            self.coal_dig_depth = float(self.lineEdit_AverageDigDepth.text())
        except Exception:
            self.outputToUser_2("煤层平均采深未设置或设置错误!", level=1)
            return

        # 4. 拐点偏移距
        try:
            self.offset_distance = float(self.lineEdit_OffsetDistance.text())
        except Exception:
            self.outputToUser_2("拐点偏移距未设置或设置错误!", level=1)
            return

        # 5. 主要影响角
        try:
            self.main_influence_angel = float(self.lineEdit_MainInfluentialAngel.text())
        except Exception:
            self.outputToUser_2("主要影响角未设置或设置错误!", level=1)
            return

        # 6. 地表移动总时间
        try:
            self.total_move_time = float(self.lineEdit_GroundMoveTotalTime.text())
        except Exception:
            self.outputToUser_2("地表移动总时间未设置或设置错误, 将根据《三下采煤规程》进行计算!", level=1)
            self.total_move_time = 2.5 * self.coal_dig_depth * 24
            self.outputToUser_2(f"地表移动总时间理论计算值为{self.total_move_time}", level=1)

        # 7.煤层法向厚度
        try:
            self.coal_vertical_thickness = float(self.lineEdit_CoalVerticalThickness.text())
        except Exception:
            self.outputToUser_2("煤层法向厚度未设置或设置错误!", level=1)
            self.coal_vertical_thickness = None

        # 8. 煤层倾角
        try:
            self.coal_angel = float(self.lineEdit_CoalAngel.text())
        except Exception:
            self.outputToUser_2("煤层倾角未设置或设置错误", level=1)
            self.coal_angel = None

        # 9. 下沉系数
        try:
            self.down_cofficient = float(self.lineEdit_CoalAngel.text())
        except Exception:
            self.outputToUser_2("下沉系数未设置或设置错误", level=1)
            self.down_cofficient = None

        if self.W0 is None:
            if not all([self.coal_vertical_thickness, self.down_cofficient, self.coal_angel]):
                self.outputToUser_2("参数不足, 无法计算沉降值!", level=1)
                return
            else:
                self.W0 = self.coal_vertical_thickness * self.down_cofficient * np.cos(self.coal_angel * 180 / np.pi)
                self.outputToUser_2(f"沉降计算值为{self.W0}", level=1)

        # 10. 数据采集时间
        try:
            self.timeinterval = float(self.lineEdit_GetDataTimeInterval.text())
        except Exception:
            self.outputToUser_2("数据采集时间间隔未设置或设置错误", level=1)
            return

        self.tao = 0.5 * 0.56 * self.total_move_time
        self.c = -self.coal_dig_speed * np.log(0.04) / (
                self.coal_dig_depth / np.tan(self.main_influence_angel / 180 * np.pi) + self.offset_distance)

        selections = self.tableWidget_SettlementDisplay_2.selectedRanges()
        if len(selections) == 0:
            self.outputToUser_2('您没有选择任何数据, 请重新选择后再试', 1)
            return

        if len(selections) >= 2:
            QMessageBox.critical(self, "出现错误", "您不能对多重区域进行此操作", QMessageBox.Yes)
            return

        data = []
        selection = selections[0]
        toprow = selection.topRow()
        bottomrow = selection.bottomRow()
        leftcolumn = selection.leftColumn()
        rightcolumn = selection.rightColumn()
        for col in range(leftcolumn, rightcolumn + 1, 1):
            lst = []
            for row in range(toprow, bottomrow + 1, 1):
                try:
                    lst.append(float(self.tableWidget_SettlementDisplay_2.item(row, col).text()))
                except Exception:
                    break
            data.append(lst)

        if len(data) != 2:
            self.outputToUser_2('预测数据选择错误, 请重新选择', 1)
            return
        else:
            x_time = data[0]
            predict_data = data[1]
        x_lst = x_time.copy()

        while round(x_time[-1], 4) < round(self.total_move_time, 4):
            x_time.append(round(x_time[-1] + self.timeinterval, 4))

        y_lst = []
        for x in x_time:
            y_lst.append(self.KnotheSegmentFunc(self.W0, self.total_move_time, self.c, self.tao, x))

        self.predictPlotWin_2.plotItem.clear()
        self.predictPlotWin_2.plotItem.addLegend()
        self.predictPlotWin_2.plotItem.plot(x=x_time, y=y_lst, pen='g', name="预测值")
        self.predictPlotWin_2.setLabel('left', self.tableWidget_SettlementDisplay_2.horizontalHeaderItem(1).text())
        self.predictPlotWin_2.setLabel('bottom', self.tableWidget_SettlementDisplay_2.horizontalHeaderItem(0).text())
        self.predictPlotWin_2.plotItem.plot(x_lst, predict_data, pen=None, symbolBrush='b', symbolPen='w', symbol='o',
                                            symbolSize=7,
                                            name="实际值")

        self.outputToUser_2("优化分段Knothe时间函数预测参数如下:")
        dct = {}
        dct['W0'] = [round(self.W0, 3)]
        dct['T'] = [round(self.total_move_time, 3)]
        dct['c'] = [round(self.c, 3)]
        dct['τ'] = [round(self.tao, 3)]
        df = self.dictToDataframe(dct)
        html = self.DataFrameToOnlyHtml(df)
        self.textBrowser_2.append(html)

        self.outputToUser_2("优化分段Knothe时间函数预测结果如下:")
        dct.clear()
        dct['时间'] = x_time
        dct['实际值'] = predict_data
        dct['预测值'] = list(map(lambda x: round(x, 3), y_lst))
        dct['误差'] = list(map(lambda x: round(x, 3), np.array(dct['预测值'][:len(dct['实际值'])]) - np.array(dct['实际值'])))
        dct['相对误差(%)'] = list(map(lambda x: round(x, 3),
                                  np.abs(np.array(dct['预测值'][:len(dct['实际值'])]) - np.array(dct['实际值'])) / np.array(
                                      dct['实际值']) * 100))
        df = self.dictToDataframe(dct)
        html = self.DataFrameToHtml(df, -1)
        self.textBrowser_2.append(html)

    @staticmethod
    def KnotheSegmentFunc(W0, T, c, tao, t):
        if t >= T:
            t = T
        if t <= tao:
            return W0 * 0.5 * (t - tao * (1 - np.exp(c * t))) / (tao * np.exp(c * tao))
        else:
            return W0 * (1 - 0.5 * np.exp(c * (tao - t)))

    @staticmethod
    def dictToDataframe(dct: dict):
        dflst = []
        for k, v in dct.items():
            tmp = {}
            tmp[k] = v
            dflst.append(pd.DataFrame(tmp))
        df = pd.concat(dflst, axis=1)
        df = df.fillna("")
        return df

    @staticmethod
    def DataFrameToOnlyHtml(df: pd.DataFrame):
        content = """"""
        columns = df.columns
        content += "<tr>"
        for title in columns:
            content += "<th>{}</th>".format(title)
        content += "</tr>"
        for i in range(len(df.index)):
            content += "<tr>"
            for j in range(len(columns)):
                content += '<td><b>{}</b></td>'.format(df.iloc[i, j])
            content += "</tr>"
        content += "</table></body>"
        return html_str + content

    def customParameterFit(self):
        try:
            W0 = float(self.lineEdit_W0_custom.text())
        except Exception:
            self.outputToUser_2("最终沉降值未设置或设置错误", level=4)
            return

        try:
            c = float(self.lineEdit_c_custom.text())
        except Exception:
            self.outputToUser_2("时间系数c未设置或设置错误", level=4)
            return

        try:
            tao = float(self.lineEdit_tao_custom.text())
        except Exception:
            self.outputToUser_2("最大下沉速度出现的时间τ未设置或设置错误", level=4)
            return

        try:
            span = float(self.lineEdit_GetDataTimeInterval_custom.text())
        except Exception:
            self.outputToUser_2("数据采集时间间隔未设置或设置错误", level=4)
            return

        try:
            T = float(self.lineEdit_GroundMoveTotalTime_custom.text())
        except Exception:
            self.outputToUser_2("地表移动总时间T未设置或设置错误", level=4)
            return

        selections = self.tableWidget_SettlementDisplay.selectedRanges()
        if len(selections) == 0:
            self.outputToUser_2('您没有选择任何数据, 请重新选择后再试', 1)
            return

        if len(selections) >= 2:
            QMessageBox.critical(self, "错误提示", "您不能对多重区域进行此操作", QMessageBox.Yes)
            return

        data = []
        selection = selections[0]
        toprow = selection.topRow()
        bottomrow = selection.bottomRow()
        leftcolumn = selection.leftColumn()
        rightcolumn = selection.rightColumn()
        for col in range(leftcolumn, rightcolumn + 1, 1):
            lst = []
            for row in range(toprow, bottomrow + 1, 1):
                try:
                    lst.append(float(self.tableWidget_SettlementDisplay_2.item(row, col).text()))
                except Exception:
                    break
            data.append(lst)

        if len(data) != 2:
            self.outputToUser_2('预测数据选择错误, 请重新选择', 1)
            return
        else:
            x_time = data[0]
            predict_data = data[1]
        x_lst = x_time.copy()

        while round(x_time[-1], 4) < round(T, 4):
            x_time.append(round(x_time[-1] + span, 4))

        y_lst = []
        for x in x_time:
            y_lst.append(self.KnotheSegmentFunc(W0, T, c, tao, x))

        self.predictPlotWin_2.plotItem.clear()
        self.predictPlotWin_2.plotItem.addLegend()
        self.predictPlotWin_2.plotItem.plot(x=x_time, y=y_lst, pen='g', name="预测值")
        self.predictPlotWin_2.setLabel('left', self.tableWidget_SettlementDisplay.horizontalHeaderItem(1).text())
        self.predictPlotWin_2.setLabel('bottom', self.tableWidget_SettlementDisplay.horizontalHeaderItem(0).text())

        self.predictPlotWin_2.plotItem.plot(x_lst, predict_data, pen=None, symbolBrush='b', symbolPen='w', symbol='o',
                                            symbolSize=7,
                                            name="实际值")

        self.outputToUser_2("Knothe时间函数自定义参数拟合结果如下:", level=2)
        dct = {}
        dct['时间'] = x_time
        dct['实际值'] = predict_data
        dct['预测值'] = list(map(lambda x: round(x, 3), y_lst))
        dct['误差'] = list(map(lambda x: round(x, 3), np.array(dct['预测值'][:len(dct['实际值'])]) - np.array(dct['实际值'])))
        dct['相对误差(%)'] = list(map(lambda x: round(x, 3),
                                  np.abs(np.array(dct['预测值'][:len(dct['实际值'])] - np.array(dct['实际值'])) / np.array(
                                      dct['实际值']) * 100)))

        df = self.dictToDataframe(dct)
        html = self.DataFrameToHtml(df, -1)
        self.textBrowser_2.append(html)

    def dynamicFit(self):

        c_begin = self.doubleSpinBox_CBegin.value()
        c_end = self.doubleSpinBox_CEnd.value()
        c_span = self.doubleSpinBox_CChangeStep.value()

        tao_begin = self.doubleSpinBox_taoBegin.value()
        tao_end = self.doubleSpinBox_taoEnd.value()
        tao_span = self.doubleSpinBox_taoChangeStep.value()

        ignore_str = self.lineEdit_ignore.text()
        ignore_lst = [-1]

        for str1 in ignore_str.split(","):
            tmp = str1.strip()
            if tmp == "":
                continue
            try:
                tmp = int(tmp)
                ignore_lst.append(tmp)
            except:
                QMessageBox.critical(self, "错误提示", "忽略数据索引错误")
                return
        try:
            time_interval = float(self.lineEdit_timeinterval.text())
        except:
            QMessageBox.critical(self, "错误提示", "数据采集时间间隔设置错误")
            return

        try:
            self.T_dynamic = float(self.lineEdit_T.text())
        except:
            QMessageBox.critical(self, "错误提示", "地表移动总时间T设置错误")
            return

        try:
            self.W0_danymic = float(self.lineEdit_FitW0.text())
        except:
            QMessageBox.critical(self, "错误提示", "地表最终沉降值W0设置错误")
            return

        selections = self.tableWidget_SettlementDisplay.selectedRanges()
        if len(selections) == 0:
            self.outputToUser_2('您没有选择任何数据, 请重新选择后再试', 1)
            return

        if len(selections) >= 2:
            QMessageBox.critical(self, "错误提示", "您不能对多重区域进行此操作", QMessageBox.Yes)
            return

        data = []
        selection = selections[0]
        toprow = selection.topRow()
        bottomrow = selection.bottomRow()
        leftcolumn = selection.leftColumn()
        rightcolumn = selection.rightColumn()
        for col in range(leftcolumn, rightcolumn + 1, 1):
            lst = []
            for row in range(toprow, bottomrow + 1, 1):
                try:
                    lst.append(float(self.tableWidget_SettlementDisplay_2.item(row, col).text()))
                except Exception:
                    break
            data.append(lst)

        if len(data) != 2:
            self.outputToUser_2('预测数据选择错误, 请重新选择', 1)
            return
        else:
            x_time = data[0]
            predict_data = data[1]

        self.x_time = x_time.copy()


        while round(x_time[-1], 4) < round(self.T_dynamic, 4):
            x_time.append(round(x_time[-1] + time_interval, 4))


        self.added_x_time = x_time  # 包含预测后部分时间段的全部时间

        self.real_data = predict_data
        start = time.time()

        self.datas, self.err_lsts, self.draw_length = calculate_numba(self.W0_danymic, c_begin, c_end, c_span, tao_begin, tao_end, tao_span, self.T_dynamic,
                  x_time, predict_data, ignore_lst)

        # 数据长度处理
        self.datas :np.ndarray= self.datas[:self.draw_length]
        self.err_lsts:np.ndarray = self.err_lsts[:self.draw_length]
        self.err_lsts = self.err_lsts.tolist()
        self.unsorted_err_lst = self.err_lsts.copy()
        self.err_lsts.sort()
        print("耗时{}s".format(time.time() - start))

        self.predictPlotWin_2.plotItem.clear()
        self.predictPlotWin_2.plotItem.addLegend()
        self.plot = self.predictPlotWin_2.plotItem.plot(pen = 'g', name = "预测值")
        self.true_plot = self.predictPlotWin_2.plotItem.plot(pen = None, symbolBrush=(0,0,200), symbolPen='w', symbol='o', symbolSize=7, name="实测值")
        self.exist_data_flag = True
        self.timer.stop()
        self.draw_cnt = 0
        self.timer.start(1)

    def start(self):
        if self.draw_cnt < self.draw_length:
            if self.radioButton_moshi1.isChecked():

                self.plot.setData(self.added_x_time, self.datas[self.draw_cnt])
                self.true_plot.setData(self.x_time, self.real_data)
                # (err_sum, max_err, np.float64(index), c, tao)
                err = self.unsorted_err_lst[self.draw_cnt]
                self.label_Show_c.setText("{:.3f}".format(err[3]))
                self.label_show_tao.setText("{:.1f}".format(err[4]))
                self.label_showRelativeErrSum.setText("{:.3f}".format(err[0]))
                self.label_showError.setText("{:.3f}".format(err[1]))
                self.draw_cnt += 1
            elif self.radioButton_moshi2.isChecked():
                cnt = self.draw_length - self.draw_cnt - 1
                self.plot.setData(self.added_x_time, self.datas[int(self.err_lsts[cnt][2])])
                self.true_plot.setData(self.x_time, self.real_data)
                # (err_sum, max_err, np.float64(index), c, tao)
                err = self.err_lsts[self.draw_cnt]
                self.label_Show_c.setText("{:.3f}".format(err[3]))
                self.label_show_tao.setText("{:.1f}".format(err[4]))
                self.label_showRelativeErrSum.setText("{:.3f}".format(err[0]))
                self.label_showError.setText("{:.3f}".format(err[1]))
                self.draw_cnt += 1
        else:
            self.timer.stop()


